﻿using LogLib;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;

namespace PassportReaderAPI
{
    /// <summary>
    /// 初始化识别核心
    /// </summary>
    /// <param name="userID">用户ID，用以校验授权。</param>
    /// <param name="nType">
    /// 每个比特位代表一种识别引擎，比特位为1 表示加
    /// 载对应的识别引擎，0 表示不加载。
    /// Bit0 代表名片识别核心；
    /// 其他比特位暂未使用，保留。
    /// </param>
    /// <param name="lpDirectory">识别核心文件存放的路径</param>
    /// <returns>
    /// 0 初始化成功
    ///1 授权ID 不正确
    ///2 设备初始化失败
    ///3 初始化核心失败
    ///4 未找到授权文件
    ///5 识别核心加载模板失败
    ///6 初始化读卡器失败
    /// </returns>
    [UnmanagedFunctionPointer(CallingConvention.StdCall)]
    public delegate int InitIDCard([MarshalAs(UnmanagedType.LPWStr)] String userID, int nType, [MarshalAs(UnmanagedType.LPWStr)] String lpDirectory);

    /// <summary>
    /// 获取识别字段名称
    /// </summary>
    /// <param name="nAttribute">
    /// 0：获取芯片字段名称
    /// 1：获取版面OCR 字段名称
    ///</param>
    /// <param name="nIndex">字段索引，详见附录B</param>
    /// <param name="ArrBuffer">存放识别结果的缓冲区</param>
    /// <param name="nBufferLen">
    /// [输入] 缓冲区大小
    /// [输出] 识别结果长度
    /// </param>
    /// <returns>
    /// 0 成功
    ///-1 识别核心未初始化
    ///-2 不存在此属性
    /// 1 缓冲区存储空间太小，nBufferLen 返回需要的空间大小
    /// 2 识别失败
    /// 3 不存在由nIndex 代表的字段
    /// </returns>
    [UnmanagedFunctionPointer(CallingConvention.StdCall)]
    public delegate int GetFieldNameEx(int nAttribute, int nIndex, [MarshalAs(UnmanagedType.LPWStr)] string ArrBuffer, ref int nBufferLen);

    /// <summary>
    /// 获取识别字段内容
    /// </summary>
    /// <param name="nAttribute">
    /// 0：获取芯片字段名称
    /// 1：获取版面OCR 字段名称
    /// </param>
    /// <param name="nIndex">字段索引，详见附录B</param>
    /// <param name="lpBuffer">存放识别结果的缓冲区</param>
    /// <param name="nBufferLen">
    /// [输入] 缓冲区大小
    /// [输出] 识别结果长度
    /// </param>
    /// <returns>
    /// 0 成功
    ///-1 识别核心未初始化
    ///-2 不存在此属性
    /// 1 缓冲区存储空间太小，nBufferLen 返回需要的空间大小
    /// 2 识别失败
    /// 3 不存在由nIndex 代表的字段
    /// </returns>
    [UnmanagedFunctionPointer(CallingConvention.StdCall)]
    public delegate int GetRecogResultEx(int nAttribute, int nIndex, [MarshalAs(UnmanagedType.LPWStr)] string lpBuffer, ref int nBufferLen);

    /// <summary>
    /// 获取设备型号
    /// </summary>
    /// <param name="ArrDeviceName">用来保存序列号的Buffer</param>
    /// <param name="nLength">lpBuffer 缓冲区的容量，以wchar_t 为单位，建议不小于64</param>
    /// <returns>
    /// true 成功
    /// false 失败
    /// </returns>
    [UnmanagedFunctionPointer(CallingConvention.StdCall)]
    public delegate int GetCurrentDevice([MarshalAs(UnmanagedType.LPWStr)] String ArrDeviceName, int nLength);

    /// <summary>
    /// 获取SDK 版本号
    /// </summary>
    /// <param name="ArrVersion">用来保存识别核心版本号的缓冲区</param>
    /// <param name="nLength">lpBuffer 缓冲区的大小</param>
    [UnmanagedFunctionPointer(CallingConvention.StdCall)]
    public delegate void GetVersionInfo([MarshalAs(UnmanagedType.LPWStr)] String ArrVersion, int nLength);

    /// <summary>
    /// 判断设备是否已连接到电脑主机
    /// </summary>
    /// <returns></returns>
    public delegate bool CheckDeviceOnline();

    /// <summary>
    /// 清空参与自动分类的证件类型
    /// </summary>
    public delegate void ResetIDCardID();

    /// <summary>
    /// 设置/增加参与自动分类的证件ID。，调用一次只能设置一种证件
    /// 类型，当要识别多种证件时，可以连续多次调用，进行累加。
    /// </summary>
    /// <param name="nMainID">参数自动分类的证件类型</param>
    /// <param name="nSubID">
    /// 参与自动分类的证件子类型列表。如果
    /// nSubIDCount 值为1 且nSubID 中第一个元素值为0,
    /// 则所有子类型都参与自动分类
    ///</param>
    /// <param name="nSubIdCount">参与自动分类的证件子类型个数</param>
    /// <returns>
    /// 0 成功
    /// 其他 失败
    /// </returns>
    public delegate int AddIDCardID(int nMainID, int[] nSubID, int nSubIdCount);

    /// <summary>
    /// 检测是否有证件放入或拿出
    /// </summary>
    /// <returns>
    /// -1 核心尚未初始化
    /// 0 未检测到证件放入或拿出
    /// 1 检测到有证件放入
    /// 2 检测到证件被拿出
    /// 3 检测到手机条码（仅支持AR、KR 系列设备）
    /// </returns>
    public delegate int DetectDocument();

    /// <summary>
    /// 设置是否读取芯片信息
    /// </summary>
    /// <param name="nDG">
    /// bit1 至bit16，每个比特位分别表示一个数据分组，比特位
    /// 取值1 表示读取对应的分组数据，0 表示不读取。
    /// bit1 表示DG1；
    /// bit2 表示DG2；
    /// ... ...
    /// bit16 表示DG16。
    /// </param>
    public delegate void SetRecogDG(int nDG);

    /// <summary>
    /// 设置是否进行版面识别
    /// </summary>
    /// <param name="bRecogVIZ">
    /// true 表示进行版面识别
    /// false 表示不进行版面识别
    /// </param>
    public delegate void SetRecogVIZ(bool bRecogVIZ);

    /// <summary>
    /// 设置要保存的图像类型
    /// </summary>
    /// <param name="nImageType">
    /// 设置采集图像的类型，bit0 到bit4 分别表示白光图
    /// 像、红外图像、紫外图像、版面头像和芯片头像，
    /// 每个比特位取值1 表示采集对应的图像，0 表示不采集。
    /// </param>
    public delegate void SetSaveImageType(int nImageType);

    /// <summary>
    /// 从配置文件中加载证件识读选项
    /// </summary>
    /// <param name="strConfigFile">配置文件的完整路径</param>
    /// <returns>
    /// 0 成功加载配置
    /// -1 读取配置文件出错或配置格式不正确
    /// </returns>
    [UnmanagedFunctionPointer(CallingConvention.StdCall)]
    public delegate int SetConfigByFile([MarshalAs(UnmanagedType.LPWStr)] String strConfigFile);

    /// <summary>
    /// 采集图像到内存
    /// </summary>
    /// <param name="nImageSizeType">
    /// 证件尺寸：
    /// 0 代表全幅面的图像，此方式下不进行图像旋转
    /// 操作；
    /// 证件识别SDK 开发手册
    /// 21
    /// 1 代表一代证类型的图像，即证件的物理尺寸和
    /// 放置方式都和一代证相同。此类型包含一代证，
    /// 驾照，行驶证。该方式下会自动对证件图像进行
    /// 旋转操作；
    /// 2 代表二代证类型的图像，即证件的物理尺寸和
    /// 放置方式都和二代证相同。此类型包含二代证背
    /// 面，二代证正面，回乡证正面，回乡证背面，香
    /// 港永久性居民身份证。该方式下会自动对证件图
    /// 像进行旋转操作；
    /// 3 代表护照类型的图像，即证件的物理尺寸和
    /// 放置方式都和护照相同。此类型包含护照，签证，
    /// 台胞证，港澳通行证，大陆居民往来台湾通行证。
    /// 该方式下会自动对证件图像进行旋转操作；
    /// 4 代表军官证类型的图像，即证件的物理尺寸和
    /// 放置方式都和军官证相同。此类型目前仅包含军
    /// 官证。该方式下会自动对证件图像进行旋转操作；
    /// 5 户口本类型的图像，即证件的物理尺寸和放置
    /// 方式都和户口本相同。此类型目前仅包含户口
    /// 本。该方式下会自动对证件图像进行旋转操作；
    /// 6 登机牌类型的图像。此类型仅含登机牌。该方
    /// 式下会自动对证件图像进行旋转操作；
    /// 7 边民证照片页；
    /// 8 边民证个人信息页；
    /// 20 代表自定义类型大小的图像，如果采用此方
    /// 式那么需要先设置采集图像的大小，见3.5.2，
    /// 默认情况下自定义类型图像的大小为全幅尺寸，
    /// 注意此方式下不会自动进行图像的旋转操作；
    /// 21 代表采集原稿尺寸的图像，该情形下会对采
    /// 集图像自动进行去黑边倾斜校正以及剪裁处理，
    /// 当使用自动分类功能时，采集图像尺寸使用此方式。
    /// </param>
    /// <returns>
    /// 0 成功
    /// 1 失败
    /// 2 设备不在线
    /// 3 参数不合法
    /// 证件识别SDK 开发手册
    /// 22
    /// 4 失败
    /// </returns>
    public delegate int AcquireImage(int nImageSizeType);

    /// <summary>
    /// 释放识别核心
    /// </summary>
    public delegate void FreeIDCard();

    /// <summary>
    /// 获取设备序列号
    /// </summary>
    /// <param name="ArrSn">用来保存序列号的Buffer</param>
    /// <param name="nLength">lpBuffer 缓冲区的大小，最大取值不能超过16， 建议取值16。</param>
    /// <returns>
    /// 0 成功
    /// 1 设备未成功加载。
    /// 2 设备不支持此操作
    /// </returns>
    [UnmanagedFunctionPointer(CallingConvention.StdCall)]
    public delegate int GetDeviceSN([MarshalAs(UnmanagedType.LPWStr)] String ArrSn, int nLength);

    /// <summary>
    /// 保存指定图像到指定路径
    /// </summary>
    /// <param name="lpFileName">将图像保存到本地文件的文件名，
    /// 图像后缀必需为 jpg、bmp 或tif，其他格式不支持</param>
    /// <param name="nType">保存图像的类型，
    /// bit0~bit4 分别表示白光图、红外
    /// 图、紫外图、版面头像和芯片头像，
    /// 比特位取值1表示保存图像，
    /// 0 表示不保存。</param>
    /// <returns></returns>
    [UnmanagedFunctionPointer(CallingConvention.StdCall)]
    public delegate int SaveImageEx([MarshalAs(UnmanagedType.LPWStr)] string lpFileName, int nType);

    /// <summary>
    /// 当检测到放入证件后，调用此接口对证件进行识读
    /// </summary>
    /// <param name="nCardType">
    /// 1 表示证件带有电子芯片
    /// 2 表示证件不带电子芯片
    /// 4 表示检测到条码
    /// 5 表示证件带有电子芯片且检测到条码
    /// 6 表示证件不带电子芯片但检测到条码</param>
    /// <returns></returns>
    public delegate int AutoProcessIDCard(ref int nCardType);

    internal static class ReaderDll
    {
        #region Static Functions
        [DllImport("Kernel32.dll")]
        public static extern IntPtr LoadLibrary(string path);

        [DllImport("Kernel32.dll")]
        public static extern bool FreeLibrary(IntPtr _hModule);

        [DllImport("Kernel32.dll")]
        private static extern IntPtr GetProcAddress(IntPtr _hModule, string procName);

        public static Delegate LoadFunction<T>(IntPtr _hModule, string functionName)
        {
            IntPtr functionAddress = GetProcAddress(_hModule, functionName);
            if (functionAddress.ToInt64() == 0)
            {
                return null;
            }
            return Marshal.GetDelegateForFunctionPointer(functionAddress, typeof(T));
        }
        #endregion

        private static bool _initOK = false;
        public static bool InitOK
        {
            get { return _initOK; }
        }

        private static string _error = "";
        public static string Error
        {
            get { return _error; }
        }

        private static IntPtr _hModule = IntPtr.Zero;

        public static InitIDCard InitIDCard;
        public static GetFieldNameEx GetFieldNameEx;
        public static GetCurrentDevice GetCurrentDevice;
        public static GetVersionInfo GetVersionInfo;
        public static CheckDeviceOnline CheckDeviceOnline;
        public static ResetIDCardID ResetIDCardID;
        public static AddIDCardID AddIDCardID;
        public static DetectDocument DetectDocument;
        public static SetRecogDG SetRecogDG;
        public static SetRecogVIZ SetRecogVIZ;
        public static SetConfigByFile SetConfigByFile;
        public static SetSaveImageType SetSaveImageType;
        public static FreeIDCard FreeIDCard;
        public static GetDeviceSN GetDeviceSN;
        public static SaveImageEx SaveImageEx;
        public static AutoProcessIDCard AutoProcessIDCard;
        public static GetRecogResultEx GetRecogResultEx;
        public static AcquireImage AcquireImage;

        private static bool LoadDll(string dllDir)
        {
            string DllPath = Path.Combine(dllDir, "IDCard.dll");
            _hModule = LoadLibrary(@DllPath);
            if (_hModule == IntPtr.Zero)
            {
                _error = "加载IDCard.dll失败,请选择dll路径";
                Log.WriteAsync(_error);
                return false;
            }
            InitIDCard = (InitIDCard)LoadFunction<InitIDCard>(_hModule, "InitIDCard");
            GetFieldNameEx = (GetFieldNameEx)LoadFunction<GetFieldNameEx>(_hModule, "GetFieldNameEx");
            GetCurrentDevice = (GetCurrentDevice)LoadFunction<GetCurrentDevice>(_hModule, "GetCurrentDevice");
            GetVersionInfo = (GetVersionInfo)LoadFunction<GetVersionInfo>(_hModule, "GetVersionInfo");
            CheckDeviceOnline = (CheckDeviceOnline)LoadFunction<CheckDeviceOnline>(_hModule, "CheckDeviceOnline");
            ResetIDCardID = (ResetIDCardID)LoadFunction<ResetIDCardID>(_hModule, "ResetIDCardID");
            AddIDCardID = (AddIDCardID)LoadFunction<AddIDCardID>(_hModule, "AddIDCardID");
            DetectDocument = (DetectDocument)LoadFunction<DetectDocument>(_hModule, "DetectDocument");
            SetRecogVIZ = (SetRecogVIZ)LoadFunction<SetRecogVIZ>(_hModule, "SetRecogVIZ");
            SetConfigByFile = (SetConfigByFile)LoadFunction<SetConfigByFile>(_hModule, "SetConfigByFile");
            SetRecogDG = (SetRecogDG)LoadFunction<SetRecogDG>(_hModule, "SetRecogDG");
            SetSaveImageType = (SetSaveImageType)LoadFunction<SetSaveImageType>(_hModule, "SetSaveImageType");
            GetDeviceSN = (GetDeviceSN)LoadFunction<GetDeviceSN>(_hModule, "GetDeviceSN");
            SaveImageEx = (SaveImageEx)LoadFunction<SaveImageEx>(_hModule, "SaveImageEx");
            AutoProcessIDCard = (AutoProcessIDCard)LoadFunction<AutoProcessIDCard>(_hModule, "AutoProcessIDCard");
            GetRecogResultEx = (GetRecogResultEx)LoadFunction<GetRecogResultEx>(_hModule, "GetRecogResultEx");
            FreeIDCard = (FreeIDCard)LoadFunction<FreeIDCard>(_hModule, "FreeIDCard");
            AcquireImage = (AcquireImage)LoadFunction<AcquireImage>(_hModule, "AcquireImage");

            if (InitIDCard == null || GetCurrentDevice == null || GetVersionInfo == null || CheckDeviceOnline == null ||
                ResetIDCardID == null || AddIDCardID == null || DetectDocument == null || GetDeviceSN == null ||
                SaveImageEx == null || FreeIDCard == null || AutoProcessIDCard == null || GetRecogResultEx == null ||
                GetFieldNameEx == null || SetRecogDG == null || SetSaveImageType == null || SetRecogVIZ == null ||
                SetConfigByFile == null)
            {
                _error = "PassportReader SDK核心模块操作方法加载失败.";
                Log.WriteAsync(_error);
                FreeLibrary(_hModule);
                return false;
            }
            return true;
        }

        public static bool LoadKernal(string dllDir, string userId)
        {
            _error = "";
            bool ret = LoadDll(dllDir);
            if (!ret)
            {
                return false;
            }
            int nRet = InitIDCard(userId, 1, dllDir);//传指定库路径
            if (nRet == 2)
            {
                nRet = InitIDCard(userId, 1, dllDir);
            }
            if (nRet != 0)
            {                            
                _error = string.Format("From Path:{1} failed to initialize the recognition engine. \r\n Return Code: {0}. ", nRet, dllDir);
                Log.WriteAsync(_error);
                return false;
            }

            string ConfigPath = Path.Combine(dllDir, "IDCardConfig.ini");
            int nConFig = SetConfigByFile(ConfigPath);
            if (nConFig != 0)
            {
                _error = string.Format("SDK读取配置文件出错或配置格式不正确.\r\n Return Code: {0}. ", nConFig);
                Log.WriteAsync(_error);
                return false;
            }
            _initOK = true;
            return true;
        }

        public static void FreeKernal()
        {
            if (_initOK)
            {
                FreeIDCard();
                InitIDCard = null;
                GetCurrentDevice = null;
                GetVersionInfo = null;
                CheckDeviceOnline = null;
                ResetIDCardID = null;
                AddIDCardID = null;
                DetectDocument = null;
                GetDeviceSN = null;
                SaveImageEx = null;
                FreeIDCard = null;
                AutoProcessIDCard = null;
                GetRecogResultEx = null;
                GetFieldNameEx = null;
                SetRecogDG = null;
                SetSaveImageType = null;
                SetRecogVIZ = null;
                SetConfigByFile = null;
                FreeLibrary(_hModule);
            }
        }

    }
}
